﻿using System;
using System.Collections.Generic;
using System.Text;

using System.Reflection;
using EnvDTE;
using System.Windows.Forms;

using System.IO;
using KeelKit.Core;

namespace KeelKit.KeelKit_CodeLineCounter
{
    public class Counter
    {
        static KeelKit.Serialization.ClcSetting  myset = null;

        internal static void DoCalcLines(TreeView tvLineCounter, KeelKit.Serialization.ClcSetting Setting)
        {
            myset = Setting;
            Common.ShowInfo(string.Format("代码行数统计器正在准备统计代码行数...\r\n参数: \r\n排除设计器文件{0};\r\n排除空行{1};\r\n排除注释{2};\r\n排除的文件类型:{3}\r\n", myset.NoDesingerFile, myset.NoEmptyLine, myset.NoExts, myset.NoRemLine));
            if (Common.chDTE.Solution.IsOpen && Common.chDTE.Solution.FileName != null && Common.chDTE.Solution.FileName.Trim() != "")
            {
                Common.ShowInfo("统计准备就绪...");
                int codecount_S = 0, emptycount_S = 0, remcount_S = 0;
                tvLineCounter.Nodes.Clear();
                FileInfo ffff = new FileInfo(Common.chDTE.Solution.FileName);
                TreeNode Solution = tvLineCounter.Nodes.Add("Solution", (string)Common.chDTE.Solution.Properties.Item("Name").Value, FilesICO.GetFileICOKey(ffff.FullName, tvLineCounter), ffff.Extension);
                int sl = 0;
                for (int i = 1; i <= Common.chDTE.Solution.Projects.Count; i++)
                {
                    CodeCount cc = new CodeCount();
                    ProjectItems pjts = Common.chDTE.Solution.Projects.Item(i).ProjectItems;
                    Project pjt = Common.chDTE.Solution.Projects.Item(i);
                    if (pjt != null && pjt.ProjectItems != null)
                    {
                        string pjtFullName = pjt.FullName;
                        string pjtName = pjt.Name;
                        string pjtFileName = pjt.FileName;
                        cc = AddProjectItemsNode(tvLineCounter, Solution , i, pjts, pjtFullName, pjtName, pjtFileName, cc);
                        sl += cc.allcount ;
                        codecount_S += cc.codecount ;
                        remcount_S += cc.remcount ;
                        emptycount_S += cc.emptycount ;
                        Solution.Text = string.Format("名称:{0} 总行数:{1}  代码:{5} 注释:{3} 空行:{4} 文件:{2}", (string)Common.chDTE.Solution.Properties.Item("Name").Value, sl, Common.chDTE.Solution.FileName, remcount_S, emptycount_S, codecount_S);
                        Common.ShowInfo(Solution.Text);
                    }
                    else
                    {
                        Common.ShowInfo(pjt != null ? string.Format("项目{0}无效，可能未加载或项目类型不受支持！", pjt.Name) : "未知项目");
                    }
                }
                Common.ShowInfo("代码行数统计完成!");
            }
            else
            {
                Common.ShowInfo("未打开解决方案或解决方案未保存!");
            }
         
        }
        /// <summary>
        /// 代码结构
        /// </summary>
        struct CodeCount
        {
            public int allcount;
            public int codecount;
            public int remcount;
            public int emptycount;
        }

        private static CodeCount AddProjectItemsNode(TreeView tvLineCounter, TreeNode Solution, int i, ProjectItems pjts, string pjtFullName, string pjtName, string pjtFileName, CodeCount ccin)
        {
            CodeCount cc = ccin;

            if (pjts.Count > 0 && pjtFullName != null && pjtFullName.Trim() != "")
            {
                FileInfo fipjt = new FileInfo(pjtFullName);
                TreeNode[] nds = Solution.Nodes.Find(pjtName, false);
                TreeNode Project = nds.Length > 0 ? nds[0] : Solution.Nodes.Add(pjtName, pjtName, FilesICO.GetFileICOKey(fipjt.FullName, tvLineCounter), fipjt.Extension == "" ? fipjt.FullName : fipjt.Extension);
                for (int pi = 1; pi <= pjts.Count; pi++)
                {
                    ProjectItem pjtitm = pjts.Item(pi);
                    CodeCount cc1 = AddProjectNode(tvLineCounter, i, Project, pjtitm);
                    cc.codecount += cc1.codecount;
                    cc.emptycount += cc1.emptycount;
                    cc.remcount += cc1.remcount;
                    cc.allcount += cc1.allcount;
                    if (pjtitm.ProjectItems != null && pjtitm.ProjectItems.Count > 0)
                    {
                        CodeCount cc2 = AddProjectItemsNode(tvLineCounter, Project, i, pjtitm.ProjectItems, pjtitm.Name, pjtitm.Name, pjtitm.get_FileNames(1), cc1);
                        cc.codecount += cc2.codecount;
                        cc.emptycount += cc2.emptycount;
                        cc.remcount += cc2.remcount;
                        cc.allcount += cc2.allcount;
                    }
                }
                Project.Text = string.Format("名称:{0} 总行数:{1} 代码:{5} 注释:{3} 空行:{4} 文件:{2}", pjtName, cc.allcount, pjtFileName, cc.remcount, cc.emptycount, cc.codecount);
                Common.ShowInfo(Project.Text);
            }
            return cc;
        }

        private static CodeCount  AddProjectNode(TreeView tvLineCounter, int i,   TreeNode Project,   ProjectItem pjtitm)
        {
            CodeCount result=new CodeCount();
            string ps = pjtitm.Name;
            int l = 0;
            System.IO.FileInfo fi_pi = new System.IO.FileInfo(pjtitm.get_FileNames(1));
            if (fi_pi.Exists || pjtitm.ProjectItems.Count > 0)
            {
                int codecount = 0, emptycount = 0, remcount = 0;
                TreeNode pjtitem = Project.Nodes.Add(ps, ps, FilesICO.GetFileICOKey(pjtitm.get_FileNames(1), tvLineCounter), fi_pi.Extension == "" ? pjtitm.get_FileNames(1) : fi_pi.Extension);
                cfLangType lt = Kit.GetProjectLangType(Common.chDTE.Solution.Projects.Item(i));
                for (short fi = 1; fi <= pjtitm.FileCount; fi++)
                {
                    string filename = pjtitm.get_FileNames(fi);
                    FileInfo fileinfo = new System.IO.FileInfo(filename);
                    bool can = CanCountCode(lt, filename);
                    if (fileinfo.Exists && Kit.GetProjectLaneExt(lt).Contains(fileinfo.Extension)  && can )
                    {
                        
                        l += GetFilesLines(filename, lt, out codecount, out remcount, out emptycount);
                    }
                }
                pjtitem.Text = string.Format("名称:{0} 总行数:{1} 代码:{5} 注释:{3} 空行:{4} 文件:{2}", ps, l, pjtitm.get_FileNames(1), remcount, emptycount, codecount);
                result.allcount  += l;
                result.codecount    += codecount;
                result.remcount  += remcount;
                result.emptycount  += emptycount;
            }
            return result;
        }

        private static bool CanCountCode(cfLangType lt, string filename)
        {
            bool can = true;
            if (myset != null)
            {
                string[] exts = Kit.GetProjectLaneExt(lt).Split(new char[] { ';' });
                foreach (string item in exts)
                {
                    if (myset.NoDesingerFile)
                    {
                        if (filename.ToLower().EndsWith(".designer" + item.ToLower()))
                        {
                            can = false;
                            break;
                        }
                    }
                    if (myset.NoExts != null && myset.NoExts.Trim() != "")
                    {
                        if (myset.NoExts.Contains(item))
                        {
                            can = false;
                            break;
                        }
                    }
                }


            }
            return can;
        }
        public static int GetFilesLines(string filename, cfLangType lt,out  int codecount ,out  int remcount ,out  int emptycount )
        {
            int result = 0;
            codecount = 0;
            emptycount = 0;
            remcount = 0;
        
            string[] lines = System.IO.File.ReadAllLines(filename, Encoding.UTF8);
            emptycount = GetEmptyCount(lines);
            switch (lt)
            {
                case cfLangType.CSharp:
                case cfLangType.CPP :
                    remcount = CheckCSRemAndCode(lines, ref codecount);
                    break;
                case cfLangType.VBDotNet:
                    remcount =CheckVBRemAndCode(lines , ref codecount );
                    break;
                case cfLangType.IronPython:
                case cfLangType.FSharp:
                default:
                    remcount = 0;
                    codecount = lines.Length;
                    break;
            }
            if (myset != null)
            {
                if (!myset.NoEmptyLine) result += emptycount;
                if (!myset.NoRemLine) result += remcount;
            }
            result += codecount;
            return result;
        }

        public static int CheckVBRemAndCode(string [] lines,ref int codecount)
        {
            int result =0;
            foreach (var itmex in lines )
            {
                string item = itmex.Trim();
                if (item!= "")
                {
                    if (item.StartsWith("REM") || item.StartsWith("'"))
                    {
                        result++;
                    }
                    else if (item.Contains("REM") || item.Contains("'"))
                    {
                        int xxxxxx = 0;
                        string[] ttt = item.Trim().Split('"');
                        for (int ix = 0; ix < ttt.Length; ix += 2)
                        {
                            if (ttt[ix].Contains("'") || ttt[ix].Contains("REM"))
                            {
                                xxxxxx++;
                                break;
                            }
                        }
                        if (xxxxxx == 0)
                        {
                            codecount++;
                        }
                        else
                        {
                            result++;
                        }
                    }
                    else
                    {
                        codecount++;
                    }
                }
                
            }
            return result ;
        }

       
        public static int GetEmptyCount(string[] lines)
        {
            int result = 0;
            foreach (var item in lines )
            {
                if (item.Trim() == "") result++;
            }
            return result;

        }
        public static int CheckCSRemAndCode(string[] lines,ref int  codecount )
        {
            int result = 0;
            bool  haveX = false ;
            for (int i = 0;  i < lines.Length  ; i++)
            {
                string code = lines[i];
                if (haveX)
                {
                    result++;
                    if (code.Contains("*/"))
                    {
                        haveX = false;
                    }

                }
                if (code.Contains("//"))
                {
                    ChkYY(ref codecount, ref result, code);
                   
                   
                }
                else if (code.Contains("/*"))
                {
                    ChkXX(ref codecount, ref result, ref haveX, code);

                }//con */
                else
                {
                    codecount++;
                }
            }
            return result;
        }

        private static void ChkYY(ref int codecount, ref int result, string code)
        {
            if (code.Trim().StartsWith("//"))
            {
                result++;


            }
            else
            {
                int xxxx = 0;
                string[] tmpxxx = code.Split('"');
                for (int ix = 0; ix < tmpxxx.Length; ix += 2)
                {
                    if (tmpxxx[ix].Replace(" ", "").Contains(";//"))
                    {
                        xxxx++;
                        break;
                    }
                }
                if (xxxx == 0)
                {
                    codecount++;
                }
                else
                {
                    result += xxxx;
                }

            }

        }

        private static void ChkXX(ref int codecount, ref int result, ref bool haveX, string code)
        {
            if (code.Trim() == "/*")
            {
                haveX = true;
                result++;
            }
            else
            {
                string[] ff = code.Split(new string[] { "/*" }, StringSplitOptions.None);
                foreach (var item in ff)
                {
                    if (item.Trim() != "")
                    {
                        int xl = item.IndexOf("*/");
                        if (xl >= 0)
                        {

                            if (item.Length > xl + "*/".Length)
                            {
                                string tmpx = item.Substring(xl + "*/".Length).Trim();
                                if (tmpx.StartsWith("//"))
                                {
                                    result++;
                                }
                                else
                                {
                                    codecount++;
                                }//tmpx

                            }//if ilxl
                        }//xl>=0

                    }//item.trim!=""
                }//foreach

                result += ff.Length - 1;
            }//else code.Trim() == "/*"
        }


      
         
    }
}
